package world3d;

import java.awt.AWTEvent;
import java.awt.event.KeyEvent;
import java.util.Enumeration;

import javax.media.j3d.Bounds;
import javax.media.j3d.Transform3D;
import javax.media.j3d.WakeupCondition;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.media.j3d.WakeupOnElapsedTime;
import javax.media.j3d.WakeupOnViewPlatformEntry;
import javax.media.j3d.WakeupOr;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import com.sun.j3d.utils.behaviors.vp.ViewPlatformBehavior;

public class AutomaticBehaviour extends ViewPlatformBehavior {
	
	private WakeupCriterion enteringBoundary;
	private WakeupCriterion elapsedTime;
	private WakeupCriterion keyPressed;
	
	private WakeupCondition criteria;
	private int timeInterval = 500;
	
	private Transform3D backInTime = new Transform3D();
	private Transform3D theMove = new Transform3D();
	
	private int nSteps = 0;
	
	private Direction direction;
	
	private enum Direction {
		FORWARD,
		LEFT,
		RIGHT,
		ROTATING_RIGHT,
		ROTATING_LEFT
	};
	
	public AutomaticBehaviour(){
		elapsedTime = new WakeupOnElapsedTime(timeInterval);
		criteria = elapsedTime;
		keyPressed = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
	}
	
	public AutomaticBehaviour(int time){
		timeInterval = time;
		elapsedTime = new WakeupOnElapsedTime(timeInterval);
		criteria = elapsedTime;
		keyPressed = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
		theMove.setTranslation(World3dConstants.FORWARD);
	}
	
	public AutomaticBehaviour(int time, Bounds bounds){
		this(time);
		enteringBoundary = new WakeupOnViewPlatformEntry(bounds);
		criteria = new WakeupOr(new WakeupCriterion[]{enteringBoundary, elapsedTime});
	}
	
	@Override
	public void initialize() {
		wakeupOn(criteria);
	}

	@Override
	public void processStimulus(Enumeration arg0){
		
		while(arg0.hasMoreElements()){
			WakeupCriterion c = (WakeupCriterion)arg0.nextElement();
			if(c instanceof WakeupOnElapsedTime){
				makeMove();
			}else if(c instanceof WakeupOnViewPlatformEntry){
				criteria = keyPressed;
			}else if(c instanceof WakeupOnAWTEvent){
				getKey((WakeupOnAWTEvent)c);
				criteria = elapsedTime;
			}else{
				System.out.println("Unexpected event");
			}
		}
		wakeupOn(criteria);
	}
	
	private void getKey(WakeupOnAWTEvent ev){
		AWTEvent[] event = ev.getAWTEvent();
		for(AWTEvent e : event){
			if(e.getID() == KeyEvent.KEY_PRESSED){
				switch(((KeyEvent)(e)).getKeyCode()){
				case KeyEvent.VK_LEFT : 
					direction = Direction.LEFT;
					break;
				case KeyEvent.VK_RIGHT :
					direction = Direction.RIGHT;
					break;
				}
			}
			nSteps = 0;
			theMove.setTranslation(World3dConstants.FORWARD);
		}
	}
	
	
	/**
	 * Some refactoring seriously needed here
	 * 
	 * 
	 */
	private void makeMove(){
		
		if((direction == Direction.ROTATING_LEFT)
				&& nSteps == 0){
			Transform3D t3d = new Transform3D();
			targetTG.getTransform(t3d);
			int roadStarts = (World3dConstants.WORLD_SIZE - World3dConstants.ROAD_LENGTH) / 2;
			t3d.lookAt(new Point3d(roadStarts - World3dConstants.CELL_SIZE / 2, 1, World3dConstants.WORLD_SIZE / 2), 
					new Point3d(roadStarts - World3dConstants.CELL_SIZE / 2 -1, 1, World3dConstants.WORLD_SIZE / 2), 
					new Vector3d(0, 1, 0));
			t3d.invert();
			targetTG.setTransform(t3d);
			criteria = new WakeupOr(new WakeupCriterion[]{enteringBoundary, elapsedTime});
		}
		
		if(direction == Direction.ROTATING_RIGHT
				&& nSteps == 0){
			Transform3D t3d = new Transform3D();
			targetTG.getTransform(t3d);
			int roadStarts = (World3dConstants.WORLD_SIZE - World3dConstants.ROAD_LENGTH) / 2;
			t3d.lookAt(new Point3d(roadStarts - World3dConstants.CELL_SIZE / 2, 1, World3dConstants.WORLD_SIZE / 2), 
					new Point3d(roadStarts - World3dConstants.CELL_SIZE / 2 +1, 1, World3dConstants.WORLD_SIZE / 2), 
					new Vector3d(0, 1, 0));
			t3d.invert();
			targetTG.setTransform(t3d);
			criteria = new WakeupOr(new WakeupCriterion[]{enteringBoundary, elapsedTime});
		}
		
		nSteps++;
		
		Transform3D t3d = new Transform3D();
		targetTG.getTransform(t3d);
		
		if(direction == Direction.LEFT && nSteps >= World3dConstants.N_STEP_BEFORE_ROTATE){
			theMove = new Transform3D();
			theMove.rotY(Math.PI / (2.0 * World3dConstants.N_STEP_ROTATING));
			direction = Direction.ROTATING_LEFT;
			nSteps = 0;
			return;
		}else if(direction == Direction.RIGHT && nSteps >= World3dConstants.N_STEP_BEFORE_ROTATE){
			theMove = new Transform3D();
			theMove.rotY(-Math.PI / (2.0 * World3dConstants.N_STEP_ROTATING));
			nSteps = 0;
			direction = Direction.ROTATING_RIGHT;
			return;
		}else if(direction == Direction.ROTATING_LEFT && nSteps > World3dConstants.N_STEP_ROTATING){
			theMove = new Transform3D();
			theMove.setTranslation(World3dConstants.FORWARD);
			direction = Direction.FORWARD;
			return;
		}else if(direction == Direction.ROTATING_RIGHT && nSteps > World3dConstants.N_STEP_ROTATING){
			theMove = new Transform3D();
			theMove.setTranslation(World3dConstants.FORWARD);
			direction = Direction.FORWARD;
			return;
		}
		
		t3d.mul(theMove);
		targetTG.setTransform(t3d);
	}
}
